這篇專注在處理 Core Data 資料轉換,為了完成真正的折線圖!
我認為目前方案一不適合,因為要花額外時間做很多時間,只剩下今天能做折線圖了,所以走方案二。
如此一來,程式執行後,Core Data 就會偵測 Model 版本,並視需要執行我們的 NSEntityMigrationPolicy。
收支記錄統計是依照「天」來統計,所以我們要用當「天」的午夜時間記錄。首先要注意的是時區,系統中所有的日期,都要以 GMT 時間儲存,只有在顯示時轉成使用者的時區,因此我們用 Calendar 轉換成午夜時間時,需注意要設定成 GMT 時間,如下:
let calendar: Calendar = {
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
return calendar
}()
接下來我們在轉換收支記錄時,便可以用下列方式,取得該收支記錄對應的午夜時間:
let midnight = calendar.startOfDay(for: sInstance.value(forKey: "createdAt") as! Date)
接下來再透過這個時間去處理統計資料,如果還沒有統計資料就新增一筆,有該「天」的統計資料就更新金額,如下:
let request = NSFetchRequest<NSManagedObject>(entityName: mapping.destinationEntityName!)
request.predicate = NSPredicate(format: "\(#keyPath(TransactionStats.date)) = %@", argumentArray: [midnight])
guard let transactionStat = try! manager.destinationContext.fetch(request).first(where: {
return $0.value(forKey: "type") as! TransactionType == sInstance.value(forKey: "type") as! TransactionType
}) else {
let dInstance = NSEntityDescription.insertNewObject(forEntityName: mapping.destinationEntityName!, into: manager.destinationContext)
dInstance.setValue(sInstance.value(forKey: "amount"), forKey: "amount")
dInstance.setValue(midnight, forKey: "date")
dInstance.setValue(sInstance.value(forKey: "type"), forKey: "type")
return
}
transactionStat.setValue((sInstance.value(forKey: "amount") as! NSDecimalNumber).adding(transactionStat.value(forKey: "amount") as! NSDecimalNumber), forKey: "amount")
備註:引以為傲的 TransactionType 因為在資料庫中是 Transformable,也就是說在 SQLite 中是二進位,根本沒辦法作為 NSPredicate 的篩選條件,這是一個嚴重的錯誤設計,必須被修正!GitHub Issue。
以上實做完成後,再把折線圖的資料換成新的資料即可。
程式碼:Github
之後再安排時間加上 XY 軸的值,下一篇先實做依照標籤篩選收支記錄,即時更新折線圖。